package achievement

import (
	"errors"
	"reflect"
	"time"

	"gddgame.cc/galaxy/common/dsl/game"
	. "gddgame.cc/galaxy/common/dsl/game/stat"
	"gddgame.cc/galaxy/utils"
	"gddgame.cc/galaxy/utils/orm"
)

type AchievementData struct {
	Achievements    []SettingAchievement
	AchievementMap  map[int]*SettingAchievement
	AchievementList []*Achievement

	env              *game.Environment
	achievementModel *orm.Model
}

var ()

func GetAchievementData(env *game.Environment, extendType reflect.Type, insideType reflect.Type) (*AchievementData, error) {
	var err error
	data := &AchievementData{env: env}

	if data.achievementModel, data.Achievements, data.AchievementMap, err = getAchievementData(env, extendType, insideType); err != nil {
		return nil, err
	}
	achievementProvider := &game.DataProvider{
		Name:     "achievement",
		Language: true,
		Array:    true,
		Get: func() (provides interface{}, err error) {
			return data.AchievementList, nil
		},
	}
	env.RegisterData(achievementProvider)
	data.AchievementList = make([]*Achievement, len(data.Achievements))
	for index := range data.Achievements {
		data.AchievementList[index] = NewAchievement(achievementProvider, &data.Achievements[index])
	}

	return data, err
}

func getAchievementData(env *game.Environment, extendType reflect.Type, insideType reflect.Type) (*orm.Model, []SettingAchievement, map[int]*SettingAchievement, error) {
	var err error

	model, err := env.GetModel(SettingAchievementName)
	if err != nil {
		return nil, nil, nil, err
	}
	var achievement []SettingAchievement
	_, err = model.Query().Order("id", orm.ASC).Find(&achievement)
	if err != nil {
		return nil, nil, nil, err
	}
	achievementMap := make(map[int]*SettingAchievement, len(achievement))
	for k := range achievement {
		if extendType != nil {
			achievement[k].Extend = utils.ToType(achievement[k].Extend, extendType)
		}
		if insideType != nil {
			achievement[k].Inside = utils.ToType(achievement[k].Inside, insideType)
		}
		achievementMap[achievement[k].Id] = &achievement[k]
	}
	return model, achievement, achievementMap, nil
}
func (achievementData *AchievementData) GetAllAchievement(updateTime *time.Time) ([]*Achievement, error) {
	if updateTime != nil {
		achievements := make([]*Achievement, 0, 5)
		for _, achievement := range achievementData.AchievementList {
			if achievement.GetExtend().HasUpdate(updateTime) {
				achievements = append(achievements, achievement)
			}
		}
		return achievements, nil
	} else {
		return achievementData.AchievementList, nil
	}
}

func (achievementData *AchievementData) ValidAchievement(achievements map[int]*UserAchievement, user *StatInfo, openId string, achievementId int) (bool, error) {
	achievement, ok := achievementData.AchievementMap[achievementId]
	if !ok {
		return false, errors.New("成就不存在")
	}

	if !user.JudgeFactor(achievement.Factors) {
		return false, nil
	}
	newAchievement := &UserAchievement{
		Id:   achievement.Id,
		Date: time.Now().UTC().String(),
	}
	achievements[achievement.Id] = newAchievement

	achievementData.env.SendEventNotice(openId, []interface{}{"achievement", newAchievement.Id})
	return true, nil
}

func (achievementData *AchievementData) ChangeAchievement(achievements map[int]*UserAchievement, data map[string]interface{}) error {
	score := 0
	for _, userAchievement := range achievements {
		achievement, ok := achievementData.AchievementMap[userAchievement.Id]
		if !ok {
			continue
		}
		score += achievement.Score
	}
	data["score"] = score
	data["achievement"] = achievements
	return nil
}
func (achievementData *AchievementData) FullAchievement(achievements map[int]*UserAchievement, user *StatInfo, data []*UserAchievement, extend interface{}) []*UserAchievement {
	for _, achievement := range achievementData.Achievements {
		goal := user.GoalFactor(achievement.Factors)
		userAchievement, ok := achievements[achievement.Id]
		if !ok {
			userAchievement = &UserAchievement{
				Id:     achievement.Id,
				Goal:   0,
				Extend: extend,
			}
		}
		userAchievement.Goal = goal
		data = append(data, userAchievement)
	}
	return data
}
